#include "Unolyzer.h"
#include "Method.h"

#include<iostream>
#include<string>

using namespace std;

string& trim(string& in_rString)
{
    // Trim Both leading and trailing spaces  
    size_t startpos = in_rString.find_first_not_of(" \t"); 
    // Find the first character position after excluding leading blank spaces  
    size_t endpos = in_rString.find_last_not_of(" \t"); 
    // Find the first character position from reverse af  
       
    // if all spaces or empty return an empty string  
    if(( string::npos == startpos ) || ( string::npos == endpos))  
    {  
        in_rString = "";  
    }  
    else
    {
        in_rString = in_rString.substr(startpos, endpos-startpos+1 ); 
    }

    return in_rString;
}

void splitAsNameAndType(string& in_sString, string& in_sName, string& in_sType)
{
    int nLen;
    int nSplitPos;

    trim(in_sString);
    nSplitPos = in_sString.find_last_of("\t ");
    nLen = in_sString.size();

    if(string::npos == nSplitPos)
        in_sType = in_sString;
    else
    {
        in_sName = in_sString.substr(nSplitPos, nLen -nSplitPos);
        in_sType = in_sString.substr(0,nSplitPos);
    }

    trim(in_sName);
    trim(in_sType);
}

Unolyzer::Unolyzer(void)
{
    m_pxMethod = NULL;
    m_nDepthMax = 0;
}

Unolyzer::~Unolyzer(void)
{
}

void Unolyzer::onNewToken(Token& inToken)
{
    if(!m_pxMethod)
        return;

    char cDelimiter=char(inToken);
    switch(cDelimiter)
    {
    case '\n':
    case '\r':
        if( 
            (m_xCurrentScope.m_eType == Scope::eScopeCommentC) ||
            (m_xCurrentScope.m_eType == Scope::eScopeCommentCPP)
            )
        {
            m_pxMethod->m_xComplexity.m_nLoCom++;
        }
        else
        {
            m_pxMethod->m_xComplexity.m_nLoC++;
        }
        break;
    
    case ';':
        if( 
            (m_xCurrentScope.m_eType != Scope::eScopeCommentC) &&
            (m_xCurrentScope.m_eType != Scope::eScopeCommentCPP)
          )
        {
            m_pxMethod->m_xComplexity.m_nStatements++;
        }
    }
}

void Unolyzer::onScopeEntry(Scope& inNewScope, Scope& inOldScope)
{
    m_xCurrentScope = inNewScope;
    Scope::Type eNewScope = inNewScope.m_eType;

    switch(eNewScope)
    {
    case Scope::eScopeGlobal:
        break;

    case Scope::eScopeBlock:
        onEntryBlock(inOldScope);
        break;

    case Scope::eScopeParen:
        onEntryParen(inOldScope);
        break;

    case Scope::eScopeCommentC:
    case Scope::eScopeCommentCPP:
        onEntryComment(inOldScope);
        break;

    default:
        break;
    }
}

void Unolyzer::onScopeExit(Scope& inNewScope, Scope& inOldScope)
{
    m_xCurrentScope = inNewScope;
    Scope::Type eNewScope = inNewScope.m_eType;

    switch(eNewScope)
    {
    case Scope::eScopeGlobal:
        onExitToGlobal(inOldScope);
        break;

    case Scope::eScopeBlock:
        onExitToBlock(inOldScope);
        break;

    default:
        break;
    }
}

void Unolyzer::onExitToGlobal(Scope& inOldScope)
{
    Scope::Type eFromScope = inOldScope.m_eType;

    switch(eFromScope)
    {
    case Scope::eScopeParen:
        onGlobalFromParen(inOldScope);
        break;

    case Scope::eScopeBlock:
        m_apxMethod.push_back(m_pxMethod);
        m_pxMethod=NULL;
        m_nDepthMax = 0;
        break;

    default:
        break;
    }
}

void Unolyzer::onExitToBlock(Scope& inOldScope)
{
    Scope::Type eFromScope = inOldScope.m_eType;

    switch(eFromScope)
    {
    case Scope::eScopeBlock:
        m_nDepthMax--;
        break;

    default:
        break;
    }

}

void Unolyzer::onGlobalFromParen(Scope& inOldScope)
{
    std::string sArgument;
    std::string sArgumentName;
    std::string sArgumentType;
    int nSplitPos;
    int nArgLen;
    Argument xArgument;
    int nFields = inOldScope.m_asSubstream.size();

    for(int nLooper=0;nLooper<nFields;nLooper++)
    {
        sArgument = inOldScope.m_asSubstream[nLooper];
        splitAsNameAndType(sArgument,sArgumentName,sArgumentType);

        xArgument.m_sName = trim(sArgumentName);
        xArgument.m_sTypePrefix = trim(sArgumentType);
        m_pxMethod->m_axArguments.push_back(xArgument);
    }
}

void Unolyzer::onEntryParen(Scope& inOldScope)
{
    Scope::Type eFromScope = inOldScope.m_eType;
    std::string sMethod = inOldScope.m_asSubstream.back();
    std::string sMethodType;
    std::string sMethodName;

    switch(eFromScope)
    {
    case Scope::eScopeGlobal:
        m_pxMethod = new Method;
        splitAsNameAndType(sMethod,sMethodName,sMethodType);
        m_pxMethod->m_sName = sMethodName;
        m_pxMethod->m_xReturn.m_sType = sMethodType;
        break;

    default:
        break;
    }
}

void Unolyzer::onEntryBlock(Scope& inOldScope)
{
    Scope::Type eFromScope = inOldScope.m_eType;

    switch(eFromScope)
    {
    case Scope::eScopeBlock:
        if(!m_pxMethod)
            break;
        m_pxMethod->m_xComplexity.m_nPaths++;
        m_nDepthMax++;
        if(m_nDepthMax > m_pxMethod->m_xComplexity.m_nMaxDepth)
            m_pxMethod->m_xComplexity.m_nMaxDepth = m_nDepthMax;
        break;

    default:
        break;
    }
}

void Unolyzer::onEntryComment(Scope& inOldScope)
{
    Scope::Type eFromScope = inOldScope.m_eType;

    switch(eFromScope)
    {
    default:
        if(m_pxMethod)
            m_pxMethod->m_xComplexity.m_nLoCom++;
        break;
    }
}

void Unolyzer::dump(bool in_bVerbose)
{
    int nMethods;
    int nArguments;
    Method* pxMethod;

//    cout<<"Name,Arguments,Paths,MaxDepth,Statements,LoC,Comments,ReturnType"<<endl;
//    cout<<endl;

    nMethods = m_apxMethod.size();
    for(int nLooper=0;nLooper<nMethods;nLooper++)
    {
        pxMethod = m_apxMethod[nLooper];
        if(!pxMethod)
            //FIXME Hot-fix to prevent crashing
            continue;

        if(in_bVerbose) cerr<<endl<<"<"<<pxMethod->m_sName;
        if(in_bVerbose) cerr<< ":"<<pxMethod->m_xReturn.m_sType<<">"<<endl;
        
        nArguments = pxMethod->m_axArguments.size();
        if(in_bVerbose) cerr<< "Arguments="<<nArguments<<endl;
        for(int nArgLooper=0;nArgLooper<nArguments;nArgLooper++)
        {
            if(in_bVerbose) cerr<<"\t<";
            if(in_bVerbose) cerr<<pxMethod->m_axArguments[nArgLooper].m_sName<<":";
            if(in_bVerbose) cerr<<pxMethod->m_axArguments[nArgLooper].m_sTypePrefix;
            if(in_bVerbose) cerr<<">"<<endl;
        }

        /* Dumping complexity */
        if(in_bVerbose) cerr<< "Complexity:"<<endl;
        if(in_bVerbose) cerr<< "\tPaths="<<pxMethod->m_xComplexity.m_nPaths<<"\n";
        if(in_bVerbose) cerr<< "\tMaxDepth="<<pxMethod->m_xComplexity.m_nMaxDepth<<"\n";
        if(in_bVerbose) cerr<< "\tStatements="<<pxMethod->m_xComplexity.m_nStatements<<"\n";
        if(in_bVerbose) cerr<< "\tLoC="<<pxMethod->m_xComplexity.m_nLoC<<"\n";
        if(in_bVerbose) cerr<< "\tLoCom="<<pxMethod->m_xComplexity.m_nLoCom<<"\n";
        if(in_bVerbose) cerr<< "Summary:(In File)"<<"\n";

//    cout<<"Name,Arguments,Paths,MaxDepth,Statements,LoC,Comments,ReturnType"<<endl;
        cout<<pxMethod->m_sName<<",";
        cout<<pxMethod->m_axArguments.size()<<",";
        cout<<pxMethod->m_xComplexity.m_nPaths<<",";
        cout<<pxMethod->m_xComplexity.m_nMaxDepth<<",";
        cout<<pxMethod->m_xComplexity.m_nStatements<<",";
        cout<<pxMethod->m_xComplexity.m_nLoC<<",";
        cout<<pxMethod->m_xComplexity.m_nLoCom<<",";
        cout<<pxMethod->m_xReturn.m_sType;
        cout<<endl;
        if(in_bVerbose) cerr<<endl;
    }
}
